package org.luxor.commons.websocket.component.exporter;

import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.websocket.WsWebSocketContainer;
import org.apache.tomcat.websocket.pojo.PojoEndpointClient;
import org.luxor.commons.websocket.annotation.WebSocketClient;
import org.luxor.commons.websocket.component.processor.WsUriProcessor;
import org.luxor.commons.websocket.config.WsClientProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.SmartLifecycle;
import org.springframework.core.annotation.AnnotationUtils;

import javax.websocket.*;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * WebSocketClient自动注册处理
 *
 * @author Mr.yan  @date 2021/9/1
 */
public class WebSocketClientExporter extends Thread implements SmartLifecycle {

    private static final StringManager sm = StringManager.getManager(WsWebSocketContainer.class);
    private static final Logger logger = LoggerFactory.getLogger(WebSocketClientExporter.class);

    private static final ExecutorService WS_CLIENT_REGISTER_THREAD = new ThreadPoolExecutor(1, 1,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>());

    private WsUriProcessor wsUriProcessor;

    public WebSocketClientExporter(WsUriProcessor wsUriProcessor) {
        this.wsUriProcessor = wsUriProcessor;
    }

    @Override
    public void start() {
        WS_CLIENT_REGISTER_THREAD.execute(this);
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            // .ignore
        }
        registerClient();
    }

    public void registerClient() {
        List<Object> clientBeans = WsClientProcessor.clientBeans();
        for (Object bean : clientBeans) {
            WebSocketClient annotation = AnnotationUtils.findAnnotation(bean.getClass(), WebSocketClient.class);
            if (annotation == null) {
                continue;
            }
            if (logger.isInfoEnabled()) {
                logger.info("Registering @WebSocketClient class: " + bean.getClass());
            }

            String endpointUrl = wsUriProcessor.determine(annotation.value());
            try {


                WebSocketContainer container = ContainerProvider.getWebSocketContainer();

                Endpoint endpoint = new PojoEndpointClient(bean, Arrays.asList(annotation.decoders()));

                Class<? extends ClientEndpointConfig.Configurator> configuratorClazz = annotation.configurator();

                ClientEndpointConfig.Configurator configurator = null;
                if (!ClientEndpointConfig.Configurator.class.equals(configuratorClazz)) {
                    try {
                        configurator = configuratorClazz.getConstructor().newInstance();
                    } catch (ReflectiveOperationException e) {
                        throw new DeploymentException(sm.getString(
                                "wsWebSocketContainer.defaultConfiguratorFail"), e);
                    }
                }

                ClientEndpointConfig.Builder builder = ClientEndpointConfig.Builder.create();
                // Avoid NPE when using RI API JAR - see BZ 56343
                if (configurator != null) {
                    builder.configurator(configurator);
                }
                ClientEndpointConfig config = builder.
                        decoders(Arrays.asList(annotation.decoders())).
                        encoders(Arrays.asList(annotation.encoders())).
                        preferredSubprotocols(Arrays.asList(annotation.subprotocols())).
                        build();
                container.connectToServer(endpoint, config, URI.create(endpointUrl));
            } catch (Exception e) {
                logger.warn(e.getMessage() + ". (" + endpointUrl + ")", e.getCause());
            }
        }
    }

    @Override
    public boolean isAutoStartup() {
        return true;
    }

    @Override
    public boolean isRunning() {
        return false;
    }

    @Override
    public void stop(Runnable callback) {

    }

    @Override
    public int getPhase() {
        return 0;
    }

}
